//---------------------------------------------------------------------------
/** 変換中ダイアログ
 */
//---------------------------------------------------------------------------
/*
 * Copyright (c) 1999 - 2005 shimitei
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "expform.h"
#include "mainform.h"
#include "misc.h"
#include "spi_manage.h"
#include "xpi_manage.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
//TDialogForm *DialogForm;
//---------------------------------------------------------------------------
__fastcall TDialogForm::TDialogForm(TComponent* Owner)
        : TForm(Owner)
{
    m_state = convertStateConverting ;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::CloseButtonClick(TObject *Sender)
{
    m_state = convertStateEnd ;
    Application->ProcessMessages() ;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::init(void)
{
    // Queueをアクティブに
    PageControl->ActivePage = QueueTabSheet;

    // ファイル登録
    do {
        FilesAddList( MainForm->SrcListBox->Items->Strings[0] ) ;
        MainForm->SrcListBox->Items->Delete( 0 ) ;
    } while (MainForm->SrcListBox->Items->Count != 0) ;

    if ( MainForm->OverWriteCB->Checked )
    {
        m_overwrite = ABC_OVERWRITE_VERIFY ;
    }
    else
    {
        m_overwrite = ABC_OVERWRITE_ALWAYS ;
    }
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::play(void)
{
    m_state = convertStateConverting ;
    AnsiString expdir = MainForm->OutEdit->Text ;
    if ( (AnsiString)expdir.AnsiLastChar() != "\\" )
        expdir += "\\" ;

while (true) {
    while ( m_state == convertStateConverting )
    {
        if ( QueueListBox->Items->Count <= 0 )
        {
            m_state = convertStateStop ;
        }
        else
        {
            AnsiString filename = QueueListBox->Items->Strings[0] ;
            SrcLabel->Caption = MinimizeName( filename, SrcLabel->Canvas, SrcLabel->Width ) ;
            Application->ProcessMessages() ;
            SetCurrentDirectory( ExtractFileDir( filename ).c_str() ) ;

            convert( filename, expdir, NULL, 0 ) ;

            QueueListBox->Items->Delete( 0 ) ;
            if ( QueueListBox->Items->Count == 0 ) m_state = convertStateStop ;
            Application->ProcessMessages() ;
            Sleep( 50 ) ;
        }
    }

    SrcLabel->Caption = "" ;

    if ( m_state == convertStateAbort )
    {
        QueueListBox->Items->Clear() ;
        ::Message( "キャンセルされました。", MB_ICONEXCLAMATION ) ;
    }
    if ( NoticeMemo->Lines->Count != 0 )
    {
        if ( (m_state == convertStateStop) || (m_state == convertStateAbort) )
        {
            m_state = convertStateSleep ;
            PageControl->ActivePage = NoticeTabSheet ;
            ::Message( "エラーがあります。", MB_ICONEXCLAMATION ) ;
            PauseButton->Enabled = false ;
            StopButton->Enabled = false ;
        }
    }
    else if ( (m_state == convertStateStop) || (m_state == convertStateAbort) )
    {
        m_state = convertStateEnd ;
    }

    while ( m_state == convertStateSleep )
    {
        Application->ProcessMessages() ;
        Sleep( 50 ) ;
    }
    if ( m_state == convertStateEnd ) break ;
}

}
//---------------------------------------------------------------------------
/** ディレクトリかチェック
 * "."(自分) や ".."(親ディレクトリ) もディレクトリとみなす
 * @param fd WIN32_FIND_DATA
 * @return ディレクトリならtrue
 */
inline bool __fastcall isDirAttribute(WIN32_FIND_DATA &fd)
{
    return ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ;
}
//---------------------------------------------------------------------------
/** 普通のディレクトリかチェック
 * この場合の特殊は "."(自分) や ".."(親ディレクトリ)
 * @param fd WIN32_FIND_DATA
 * @return 普通のディレクトリならtrue
 */
inline bool __fastcall isNotDotDir(WIN32_FIND_DATA &fd)
{
    bool result = true ;

    // "."(自分),".."(親ディレクトリ)をはじく
    if (   strcmp( fd.cFileName, "." ) == 0
        || strcmp( fd.cFileName, ".." ) == 0
       )
    {
        result = false ;
    }

    return result ;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::FilesAddList(const AnsiString &path)
{
/** 指定パスのファイルを追加
 * ワイルドカード有効
 */
    WIN32_FIND_DATA fd ;
    AnsiString DirName = ExtractFilePath( path ) ;
    HANDLE hf = FindFirstFile( path.c_str(), &fd ) ;
    if ( hf == INVALID_HANDLE_VALUE ) return ;

    do {
        if ( ::isDirAttribute( fd ) )
        {
            if ( ::isNotDotDir( fd ) )
            {
                // フォルダの追加は後回し
                MainForm->SrcListBox->Items->Insert( 1, DirName + fd.cFileName + "\\*.*" ) ;
            }
        }
        else
        {
            QueueListBox->Items->Add( DirName + fd.cFileName ) ;
        }
    } while ( FindNextFile( hf, &fd ) ) ;
    FindClose( hf ) ;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::AddErr(const int err, const AnsiString &filepath, const AnsiString &spiname)
{
/** エラーを記録
 */
    AnsiString errStr ;
    switch ( err )
    {
        case SPI_FATAL_ERR:
            errStr = "Ｓ例外" ;
            break ;
        case SPI_NO_FUNCTION:
            errStr = "Ｓ機能無し" ;
            break ;
        case SPI_ABORT:
            errStr = "Ｓキャンセル" ;
            break ;
        case SPI_NOT_SUPPORT:
            errStr = "Ｓ未知の形式" ;
            break ;
        case SPI_OUT_OF_ORDER:
            errStr = "Ｓ破損ファイル" ;
            break ;
        case SPI_NO_MEMORY:
        case SPI_MEMORY_ERROR:
            errStr = "Ｓメモリエラー" ;
            break ;
        case SPI_FILE_READ_ERROR:
            errStr = "Ｓ読み込みエラー" ;
            break ;
        case SPI_OTHER_ERROR:
            errStr = "Ｓ内部エラー" ;
            break ;
        case SPI_FILE_WRITE_ERROR:
            errStr = "Ｓ書き込みエラー" ;
            break ;
        case SPI_END_OF_FILE:
            errStr = "Ｓ終端エラー" ;
            break ;
        case XPI_FATAL_ERR:
            errStr = "Ｘ例外" ;
            break ;
        case XPI_NO_FUNCTION:
            errStr = "Ｘ機能無し" ;
            break ;
        case XPI_ABORT:
            errStr = "Ｘキャンセル" ;
            break ;
        case XPI_NOT_SUPPORT:
            errStr = "Ｘ未知の形式" ;
            break ;
        case XPI_OUT_OF_ORDER:
            errStr = "Ｘ破損ファイル" ;
            break ;
        case XPI_NO_MEMORY:
        case XPI_MEMORY_ERROR:
            errStr = "Ｘメモリエラー" ;
            break ;
        case XPI_FILE_READ_ERROR:
            errStr = "Ｘ読み込みエラー" ;
            break ;
        case XPI_OTHER_ERROR:
            errStr = "Ｘ内部エラー" ;
            break ;
        case XPI_FILE_WRITE_ERROR:
            errStr = "Ｘ書き込みエラー" ;
            break ;
        case XPI_END_OF_FILE:
            errStr = "Ｘ終端エラー" ;
            break ;
        default:
            errStr = "未定義エラー(" ;
            errStr += err +")" ;
    }
    errStr += "：" + filepath + "[" + ExtractFileName( spiname ) + "]" ;
    NoticeMemo->Lines->Add( errStr ) ;
}
//---------------------------------------------------------------------------
int __fastcall TDialogForm::convert(AnsiString srcpath, AnsiString desdir, char *pmem, long len)
{
/** 対応チェック
 */
    int ret ;
    APluginSusie *pspi = g_spiManager.filecheck( srcpath.c_str(), pmem ) ;
    if ( pspi == NULL )
    {
        // 対応プラグイン無し
        ret = SPI_NOT_SUPPORT ;
        AddErr( ret, srcpath, "ABC.EXE" ) ;
        return ret ;
    }
if ( pspi->getType() == TYPE_00IN ) {
        // 変換保存
        SrcLabel->Caption = MinimizeName(srcpath, SrcLabel->Canvas, SrcLabel->Width);
Application->ProcessMessages();
        ret = spi2xpi(srcpath.c_str(), desdir.c_str(), pmem, len, pspi);
        if (ret != XPI_ALL_RIGHT) {
            AddErr(ret, srcpath, pspi->getPluginFilename());
        }
} else {
        //アーカイブ
        HLOCAL hInfo;
        HLOCAL hfile;
        fileInfo *pfinfo;
        long datasize;
        char *pdata;
    if (pmem == NULL) {
        hInfo = NULL;
        ret = g_spiManager.spi_arcinfo(pspi, srcpath.c_str(), 0, 0, &hInfo);
        if (ret == SPI_ALL_RIGHT || hInfo != NULL) {
            pfinfo = (fileInfo *)LocalLock(hInfo);
            while (pfinfo->method[0] != '\0' && m_state != convertStateAbort) {
                ret = g_spiManager.spi_getfile(pspi, srcpath.c_str(), pfinfo->position, (char *)&hfile, 0x0100, NULL, 0);
                if (ret == SPI_ALL_RIGHT) {
                    datasize = pfinfo->filesize;
                    if (datasize <= 0) {
                        datasize = LocalSize(hfile);
                    }
                    pdata = (char *)LocalLock(hfile);
                    int reterr;
                    reterr = convert(srcpath +"\\" +pfinfo->path +pfinfo->filename, ExtractFilePath(desdir +ExtractFileName(srcpath) +"\\" +pfinfo->path +pfinfo->filename), pdata, datasize);
                    if (reterr != XPI_ALL_RIGHT && MainForm->UnknownOutputCB->Checked == true) {
                        HANDLE hf;
                        AnsiString errfname;
                        errfname = desdir +ExtractFileName(srcpath) +"\\" +pfinfo->path +pfinfo->filename;
                        //ディレクトリ作成
                        ForceDirectories(ExtractFileDir(errfname));
                        hf = CreateFile(errfname.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
                        reterr = 1;
                        if (hf != INVALID_HANDLE_VALUE) {
                            DWORD WriteBytes;
                            pdata = (char *)LocalLock(hfile);
                            if (!WriteFile(hf, pdata, datasize, &WriteBytes, NULL)) {
                                reterr = 0;
                            }
                            LocalUnlock(hfile);
                            CloseHandle(hf);
                        } else {
                            AddErr(XPI_FILE_WRITE_ERROR, errfname, "ABC.EXE");
                        }
                        if (reterr == 0)
                            DeleteFile(errfname.c_str());
                    }
                    LocalFree(hfile);
                }
                pfinfo++;
    Application->ProcessMessages();
            }
            LocalUnlock(hInfo);
            LocalFree(hInfo);
        }
    } else {
        hInfo = NULL;
        ret = g_spiManager.spi_arcinfo(pspi, pmem, len, 1, &hInfo);
        if (ret == SPI_ALL_RIGHT || hInfo != NULL) {
            pfinfo = (fileInfo *)LocalLock(hInfo);
            while (pfinfo->method[0] != '\0' && m_state != convertStateAbort) {
                ret = g_spiManager.spi_getfile(pspi, pmem +pfinfo->position, len -pfinfo->position,
                                       (char *)&hfile, 0x0101, NULL, 0);
                if (ret == SPI_ALL_RIGHT) {
                    datasize = pfinfo->filesize;
                    if (datasize <= 0) {
                        datasize = LocalSize(hfile);
                    }
                    pdata = (char *)LocalLock(hfile);
                    int reterr;
                    reterr = convert(srcpath +"\\" +pfinfo->path +pfinfo->filename, ExtractFilePath(desdir +ExtractFileName(srcpath) +"\\" +pfinfo->path +pfinfo->filename), pdata, datasize);
                    if (reterr != XPI_ALL_RIGHT && MainForm->UnknownOutputCB->Checked == true) {
                        HANDLE hf;
                        AnsiString errfname;
                        errfname = desdir +ExtractFileName(srcpath) +"\\" +pfinfo->path +pfinfo->filename;
                        //ディレクトリ作成
                        ForceDirectories(ExtractFileDir(errfname));
                        hf = CreateFile(errfname.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
                        reterr = 1;
                        if (hf != INVALID_HANDLE_VALUE) {
                            DWORD WriteBytes;
                            pdata = (char *)LocalLock(hfile);
                            if (!WriteFile(hf, pdata, datasize, &WriteBytes, NULL)) {
                                reterr = 0;
                            }
                            LocalUnlock(hfile);
                            CloseHandle(hf);
                        } else {
                            AddErr(XPI_FILE_WRITE_ERROR, errfname, "ABC.EXE");
                        }
                        if (reterr == 0)
                            DeleteFile(errfname.c_str());
                    }
                    LocalFree(hfile);
                }
                pfinfo++;
    Application->ProcessMessages();
            }
            LocalUnlock(hInfo);
            LocalFree(hInfo);
        }
    }
    if (ret != XPI_ALL_RIGHT) {
        AddErr(ret, srcpath, pspi->getPluginFilename());
    }
}

    return ret;
}
//---------------------------------------------------------------------------
int __fastcall TDialogForm::spi2xpi(char *srcpath, char *desdir, char *mem, long len, APluginSusie *pspi)
{
/** 変換
 */
    PictureInfo	picinfo;
    int	ret;
    HANDLE bmpdata = NULL;
    HANDLE bmpinfo = NULL;
    memset(&picinfo, 0, sizeof(PictureInfo));

    //ディレクトリ作成
    ForceDirectories(desdir);

if (mem == NULL) {
    g_spiManager.spi_picinfo(pspi, srcpath, 0, 0, &picinfo);
    //GetPictureInfoはエラーでも構わない
        // 画像のロード
        ret = g_spiManager.spi_pic(pspi, srcpath, 0, 0, &bmpinfo, &bmpdata, NULL, 0);
        if (ret == SPI_NO_FUNCTION) { //ファイル入力には対応していないが、メモリ入力には対応している？
            HANDLE hf;
            hf = CreateFile(srcpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
            if (hf != INVALID_HANDLE_VALUE) {
                DWORD fileBytes = GetFileSize(hf, NULL);
                HLOCAL hmem = LocalAlloc(LMEM_FIXED, fileBytes);
                if (hmem != NULL) {
                    DWORD readBytes;
                    if (!ReadFile(hf, hmem, readBytes, &fileBytes, NULL)) {
                        ret = spi2xpi(srcpath, desdir, (char*)hmem, readBytes, pspi);
                    }
                    LocalFree(hmem);
                }
                CloseHandle(hf);
            }
        }
} else { //メモリ入力
        g_spiManager.spi_picinfo(pspi, mem, len, 1, &picinfo);
        // 画像のロード
        ret = g_spiManager.spi_pic(pspi, mem, len, 1, &bmpinfo, &bmpdata, NULL, 0);
}

if (ret == SPI_ALL_RIGHT) {
    //色数調整
    int orgc;// = picinfo.colorDepth; //実際の色数と異なるばあいがある
    BITMAPINFOHEADER *pbminfo = (BITMAPINFOHEADER *)LocalLock(bmpinfo);
    orgc = pbminfo->biBitCount;
    //PictureInfoはDIBに合わせてしまう
    //picinfo.left = 0;
    //picinfo.top = 0;
    picinfo.width = pbminfo->biWidth;
    picinfo.height = pbminfo->biHeight;
    //picinfo.x_density = 0;
    //picinfo.y_density = 0;
    picinfo.colorDepth = pbminfo->biBitCount;
    //picinfo.hInfo = NULL;
    LocalUnlock(bmpinfo);
    int desc = 0;
    if (orgc < 16) {
        ret = g_xpiManager.checkSupport8(orgc);
    } else {
        ret = g_xpiManager.checkSupport16(orgc);
    }
    if (ret == 0) {
        //8ビットや24ビットではどうか？
        if (orgc < 8) {
            ret = g_xpiManager.checkSupport8(8);
            if (ret != 0) {
                desc = 8;
            }
        }
        if (desc == 0) {
            if (orgc < 16) {
                ret = g_xpiManager.checkSupport8(24);
            } else {
                ret = g_xpiManager.checkSupport16(24);
            }
            if (ret != 0) {
                desc = 24;
            }
        }
    } else {
        desc = orgc;
    }
if (desc == 0) {
            ret = XPI_NOT_SUPPORT;
} else {
        if (orgc != desc) {
            //増色
            if (desc == 8) {
                //8ビットへ
                to8bit(&bmpinfo, &bmpdata, orgc);
                picinfo.colorDepth = 8;
            } else if (desc == 24) {
                //24ビットへ
                to24bit(&bmpinfo, &bmpdata, orgc);
                picinfo.colorDepth = 24;
            }
        }
        if ( ret != 0 )
        {
            // 画像の変換と保存
            AnsiString ext = g_xpiManager.getXpiInfo( -1, 3 ) ;
            AnsiString outname = desdir + ExtractFileName( ChangeFileExt( srcpath, ext ) ) ;
            ret = g_xpiManager.convertPicture( outname.c_str(), 0, &bmpinfo, &bmpdata, &picinfo, NULL, 0 ) ;
            if ( ret != XPI_ALL_RIGHT )
            {
                //念のため削除を試みる
                DeleteFile(outname.c_str());
            }
        } else {
            ret = XPI_NOT_SUPPORT;
        }
}

        // 後始末
        if (bmpdata != NULL) LocalFree(bmpdata);
        if (bmpinfo != NULL) LocalFree(bmpinfo);
}
    if (picinfo.hInfo != NULL) LocalFree(picinfo.hInfo); // TEXT info 解放

    return ret;
}
//---------------------------------------------------------------------------
int __fastcall TDialogForm::to8bit(HLOCAL *bmpinfo, HLOCAL *bmpdata, const int color)
{
/** 色深度が8bit未満を8bitに増色する
 * 成功は非0を返す
 */
    HLOCAL newinfo, newdata;
    unsigned char *pdata;
    BITMAPINFO *pnewinfo;
    BITMAPINFOHEADER *pnewih;

    BITMAPINFO *pbminfo;
    BITMAPINFOHEADER *pbmih;
    pbminfo = (BITMAPINFO *)LocalLock(*bmpinfo);
    pbmih = &pbminfo->bmiHeader;
    if (pbmih->biCompression != BI_RGB) {
        LocalUnlock(*bmpinfo);
        return 0;
    }
    unsigned char *pbmp;
    pbmp = (unsigned char *)LocalLock(*bmpdata);

    //差し替えるデータ領域を確保
    newinfo = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(BITMAPINFOHEADER) +sizeof(RGBQUAD)*256);
    if (newinfo == NULL) {
        LocalUnlock(*bmpinfo);
        LocalUnlock(*bmpdata);
        return 0;
    }
    int fixwidth = (pbmih->biWidth +3) & ~3; //4バイト境界にあわせる
    unsigned int imgsize = fixwidth * abs(pbmih->biHeight); //Heightは負もあるかも?
    newdata = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, imgsize);
    if (newdata == NULL) {
        LocalUnlock(*bmpinfo);
        LocalUnlock(*bmpdata);
        LocalFree(newinfo);
        return 0;
    }
    pdata = (unsigned char *)LocalLock(newdata);
    pnewinfo = (BITMAPINFO *)LocalLock(newinfo);
    pnewih = &pnewinfo->bmiHeader;

    // BITMAPINFOを作成
    pnewih->biSize = sizeof(BITMAPINFOHEADER);
    pnewih->biWidth = pbmih->biWidth;
    pnewih->biHeight = pbmih->biHeight;
    pnewih->biPlanes = 1;
    pnewih->biBitCount = 8;
    pnewih->biCompression = BI_RGB;
    pnewih->biSizeImage = imgsize;
    pnewih->biXPelsPerMeter = 0;
    pnewih->biYPelsPerMeter = 0;
    pnewih->biClrUsed = 0;
    pnewih->biClrImportant = 0;

    //データ変換
    unsigned char *psrc;
    unsigned char *pdes;
    unsigned char tbyte;
    int srcfixwidth;
    if  (pbmih->biBitCount == 1) { //1bit
        //パレットコピー
        memcpy(pnewinfo->bmiColors, pbminfo->bmiColors, sizeof(RGBQUAD)*2);
        srcfixwidth  = (pbmih->biWidth +7) & ~7; //8で割り切れるように
        srcfixwidth  = (srcfixwidth /8 +3) & ~3; //4バイト境界
        for (int y=0; y < pbmih->biHeight; y++) {
            psrc = pbmp +srcfixwidth * y;
            pdes = pdata +fixwidth * y;
            for (int x=0; x < (pbmih->biWidth >> 3); x++) {
                tbyte = *(psrc++);
                *(pdes++) = ((tbyte & 0x80) != 0);
                *(pdes++) = ((tbyte & 0x40) != 0);
                *(pdes++) = ((tbyte & 0x20) != 0);
                *(pdes++) = ((tbyte & 0x10) != 0);
                *(pdes++) = ((tbyte & 0x08) != 0);
                *(pdes++) = ((tbyte & 0x04) != 0);
                *(pdes++) = ((tbyte & 0x02) != 0);
                *(pdes++) = ((tbyte & 0x01) != 0);
            }
            if (pbmih->biWidth & 7) {
                tbyte = *psrc;
                for (int z=0; z < (pbmih->biWidth & 7); z++) {
                    *(pdes++) = ((tbyte & (0x80 >> z)) != 0);
                }
            }
        }
    } else { //4bit
        //パレットコピー
        memcpy(pnewinfo->bmiColors, pbminfo->bmiColors, sizeof(RGBQUAD)*16);
        srcfixwidth  = (pbmih->biWidth +1) & ~1; //2で割り切れるように
        srcfixwidth  = (srcfixwidth /2 +3) & ~3; //4バイト境界
        for (int y=0; y < pbmih->biHeight; y++) {
            psrc = pbmp +srcfixwidth * y;
            pdes = pdata +fixwidth * y;
            for (int x=0; x < (pbmih->biWidth >> 1); x++) {
                tbyte = *(psrc++);
                *(pdes++) = (tbyte >> 4);
                *(pdes++) = (tbyte & 0x0f);
            }
            if (pbmih->biWidth & 1) {
                tbyte = *psrc;
                *pdes = (tbyte >> 4);
            }
        }
    }

    //前のを破棄してデータ差し替え
    LocalUnlock(*bmpinfo);
    LocalUnlock(*bmpdata);
    LocalUnlock(newinfo);
    LocalUnlock(newdata);
    LocalFree(*bmpinfo);
    LocalFree(*bmpdata);
    *bmpinfo = newinfo;
    *bmpdata = newdata;

    return 1;
}
//---------------------------------------------------------------------------
int __fastcall TDialogForm::to24bit(HLOCAL *bmpinfo, HLOCAL *bmpdata, const int color)
{
/** 色深度が24bit未満を24bitに増色する
 * 成功は非0を返す
 */
    HLOCAL newinfo, newdata;
    unsigned char *pdata;
    BITMAPINFO *pnewinfo;
    BITMAPINFOHEADER *pnewih;

    BITMAPINFO *pbminfo;
    BITMAPINFOHEADER *pbmih;
    pbminfo = (BITMAPINFO *)LocalLock(*bmpinfo);
    pbmih = &pbminfo->bmiHeader;
    if (   pbmih->biCompression != BI_RGB
        && pbmih->biCompression != BI_BITFIELDS) {
        LocalUnlock(*bmpinfo);
        return 0;
    }
    unsigned char *pbmp;
    pbmp = (unsigned char *)LocalLock(*bmpdata);

    //差し替えるデータ領域を確保
    newinfo = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(BITMAPINFOHEADER));
    if (newinfo == NULL) {
        LocalUnlock(*bmpinfo);
        LocalUnlock(*bmpdata);
        return 0;
    }
    int fixwidth = (pbmih->biWidth *3 +3) & ~3; //4バイト境界にあわせる
    unsigned int imgsize = fixwidth * abs(pbmih->biHeight); //Heightは負もあるかも?
    newdata = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, imgsize);
    if (newdata == NULL) {
        LocalUnlock(*bmpinfo);
        LocalUnlock(*bmpdata);
        LocalFree(newinfo);
        return 0;
    }
    pdata = (unsigned char *)LocalLock(newdata);
    pnewinfo = (BITMAPINFO *)LocalLock(newinfo);
    pnewih = &pnewinfo->bmiHeader;

    // BITMAPINFOを作成
    pnewih->biSize = sizeof(BITMAPINFOHEADER);
    pnewih->biWidth = pbmih->biWidth;
    pnewih->biHeight = pbmih->biHeight;
    pnewih->biPlanes = 1;
    pnewih->biBitCount = 24;
    pnewih->biCompression = BI_RGB;
    pnewih->biSizeImage = imgsize;
    pnewih->biXPelsPerMeter = 0;
    pnewih->biYPelsPerMeter = 0;
    pnewih->biClrUsed = 0;
    pnewih->biClrImportant = 0;

    //データ変換
    unsigned char *psrc;
    unsigned char *pdes;
    unsigned char tbyte;
    int srcfixwidth;
    if  (pbmih->biBitCount == 1) { //1bit
        RGBQUAD *ppal = pbminfo->bmiColors; //パレットへのポインタ
        srcfixwidth  = (pbmih->biWidth +7) & ~7; //8で割り切れるように
        srcfixwidth  = (srcfixwidth /8 +3) & ~3; //4バイト境界
        for (int y=0; y < pbmih->biHeight; y++) {
            psrc = pbmp +srcfixwidth * y;
            pdes = pdata +fixwidth * y;
            for (int x=0; x < (pbmih->biWidth >> 3); x++) {
                RGBQUAD *p;
                tbyte = *(psrc++);
                for (int i=0; i < 8; i++) {
                    p = ppal +((tbyte & (0x80 >> i)) != 0);
                    *(pdes++) = p->rgbBlue;
                    *(pdes++) = p->rgbGreen;
                    *(pdes++) = p->rgbRed;
                }
            }
            if (pbmih->biWidth & 7) {
                RGBQUAD *p;
                tbyte = *psrc;
                for (int i=0; i < (pbmih->biWidth & 7); i++) {
                    p = ppal +((tbyte & (0x80 >> i)) != 0);
                    *(pdes++) = p->rgbBlue;
                    *(pdes++) = p->rgbGreen;
                    *(pdes++) = p->rgbRed;
                }
            }
        }
    } else if  (pbmih->biBitCount == 4) { //4bit
        RGBQUAD *ppal = pbminfo->bmiColors; //パレットへのポインタ
        srcfixwidth  = (pbmih->biWidth +1) & ~1; //2で割り切れるように
        srcfixwidth  = (srcfixwidth /2 +3) & ~3; //4バイト境界
        for (int y=0; y < pbmih->biHeight; y++) {
            psrc = pbmp +srcfixwidth * y;
            pdes = pdata +fixwidth * y;
            for (int x=0; x < (pbmih->biWidth >> 1); x++) {
                RGBQUAD *p;
                tbyte = *(psrc++);
                p = ppal +(tbyte >> 4);
                *(pdes++) = p->rgbBlue;
                *(pdes++) = p->rgbGreen;
                *(pdes++) = p->rgbRed;
                p = ppal+(tbyte & 0xf);
                *(pdes++) = p->rgbBlue;
                *(pdes++) = p->rgbGreen;
                *(pdes++) = p->rgbRed;
            }
            if (pbmih->biWidth & 1) { //半端
                RGBQUAD *p;
                tbyte = *psrc;
                p = ppal +(tbyte >> 4);
                *(pdes++) = p->rgbBlue;
                *(pdes++) = p->rgbGreen;
                *pdes = p->rgbRed;
            }
        }
    } else if  (pbmih->biBitCount == 8) { //8bit
        RGBQUAD *ppal = pbminfo->bmiColors; //パレットへのポインタ
        srcfixwidth  = pbmih->biWidth;
        srcfixwidth  = (srcfixwidth +3) & ~3; //4バイト境界
        for (int y=0; y < pbmih->biHeight; y++) {
            psrc = pbmp +srcfixwidth * y;
            pdes = pdata +fixwidth * y;
            for (int x=0; x < pbmih->biWidth; x++) {
                RGBQUAD *p;
                tbyte = *(psrc++);
                p = ppal +tbyte;
                *(pdes++) = p->rgbBlue;
                *(pdes++) = p->rgbGreen;
                *(pdes++) = p->rgbRed;
            }
        }
    } else if  (pbmih->biBitCount == 16) { //16bit
        unsigned short r_mask;
        unsigned short g_mask;
        unsigned short b_mask;
        unsigned short tword;
        srcfixwidth  = pbmih->biWidth;
        srcfixwidth  = (srcfixwidth *2 +3) & ~3; //4バイト境界
        int r_shift = 0;
        int g_shift = 0;
        int b_shift = 0;
        int r_bit = 0;
        int g_bit = 0;
        int b_bit = 0;
        if (pbmih->biCompression == BI_BITFIELDS) {
            unsigned int *mask = (unsigned int *)(pbminfo->bmiColors);
            r_mask = *(mask +0);
            g_mask = *(mask +1);
            b_mask = *(mask +2);
            while (((r_mask >> r_shift) & 1) == 0) r_shift++;
            while (((g_mask >> g_shift) & 1) == 0) g_shift++;
            while (((b_mask >> b_shift) & 1) == 0) b_shift++;
            while (((r_mask >> (r_shift +r_bit)) & 1) == 1) r_bit++;
            while (((g_mask >> (g_shift +g_bit)) & 1) == 1) g_bit++;
            while (((b_mask >> (b_shift +b_bit)) & 1) == 1) b_bit++;
        } else {
            //555
            r_mask = 0x7c00;
            g_mask = 0x03e0;
            b_mask = 0x001f;
            r_shift = 10;
            g_shift = 5;
            b_shift = 0;
            r_bit = 5;
            g_bit = 5;
            b_bit = 5;
        }
        for (int y=0; y < pbmih->biHeight; y++) {
            psrc = pbmp +srcfixwidth * y;
            pdes = pdata +fixwidth * y;
            for (int x=0; x < pbmih->biWidth; x++) {
                unsigned char r,g,b;
                tword = *((unsigned short *)psrc);
                psrc += 2;
                b = ((tword & b_mask) >> b_shift) << (8 -b_bit);
                g = ((tword & g_mask) >> g_shift) << (8 -g_bit);
                r = ((tword & r_mask) >> r_shift) << (8 -r_bit);
                //単にシフトしただけでは色が少し変わる。
                //シフトした分上位からもってくると良いみたい。
                b = b | (b >> b_bit);
                g = g | (g >> g_bit);
                r = r | (r >> r_bit);
                *(pdes++) = b;
                *(pdes++) = g;
                *(pdes++) = r;
            }
        }
    } else { //32bit
        unsigned int r_mask;
        unsigned int g_mask;
        unsigned int b_mask;
        unsigned int tdword;
        srcfixwidth = pbmih->biWidth *4; //4バイト境界だね
        int r_shift = 0;
        int g_shift = 0;
        int b_shift = 0;
        int r_bit = 0;
        int g_bit = 0;
        int b_bit = 0;
        if (pbmih->biCompression == BI_BITFIELDS) {
            unsigned int *mask = (unsigned int *)(pbminfo->bmiColors);
            r_mask = *(mask +0);
            g_mask = *(mask +1);
            b_mask = *(mask +2);
            while (((r_mask >> r_shift) & 1) == 0) r_shift++;
            while (((g_mask >> g_shift) & 1) == 0) g_shift++;
            while (((b_mask >> b_shift) & 1) == 0) b_shift++;
            while (((r_mask >> (r_shift +r_bit)) & 1) == 1) r_bit++;
            while (((g_mask >> (g_shift +g_bit)) & 1) == 1) g_bit++;
            while (((b_mask >> (b_shift +b_bit)) & 1) == 1) b_bit++;
        } else {
            r_mask = 0x00ff0000;
            g_mask = 0x0000ff00;
            b_mask = 0x000000ff;
            r_shift = 16;
            g_shift = 8;
            b_shift = 0;
            r_bit = 8;
            g_bit = 8;
            b_bit = 8;
        }
        psrc = pbmp;
        for (int y=0; y < pbmih->biHeight; y++) {
            pdes = pdata +fixwidth * y;
            for (int x=0; x < pbmih->biWidth; x++) {
                tdword = *((unsigned int *)psrc);
                psrc += 4;
                *(pdes++) = (unsigned char)(((tdword & b_mask) >> b_shift) << (8 -b_bit));
                *(pdes++) = (unsigned char)(((tdword & g_mask) >> g_shift) << (8 -g_bit));
                *(pdes++) = (unsigned char)(((tdword & r_mask) >> r_shift) << (8 -r_bit));
            }
        }
    }

    //前のを破棄してデータ差し替え
    LocalUnlock(*bmpinfo);
    LocalUnlock(*bmpdata);
    LocalUnlock(newinfo);
    LocalUnlock(newdata);
    LocalFree(*bmpinfo);
    LocalFree(*bmpdata);
    *bmpinfo = newinfo;
    *bmpdata = newdata;

    return 1;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::StopButtonClick(TObject *Sender)
{
    m_state = convertStateAbort ;
    Application->ProcessMessages() ;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::PauseButtonClick(TObject *Sender)
{
    if ( m_state == convertStateConverting )
    {
        m_state = convertStateSleep ;
        PauseButton->Caption = "再開" ;
    }
    else if ( m_state == convertStateSleep )
    {
        m_state = convertStateConverting ;
        PauseButton->Caption = "一時停止" ;
    }
    Application->ProcessMessages() ;
}
//---------------------------------------------------------------------------
void __fastcall TDialogForm::QueueListBoxClick(TObject *Sender)
{
/** 文の後半が見えない場合のためにヒントを表示
 */
    TListBox *listBox = (TListBox *)Sender ;
    listBox->ShowHint = true;
    listBox->Hint = listBox->Items->Strings[listBox->ItemIndex];
}
//---------------------------------------------------------------------------




